package com.novunda.platform.services;


import com.novunda.platform.common.exception.StatisticsException;
import com.novunda.platform.common.handler.LogCallBack;
import com.novunda.platform.entities.Log;
import com.novunda.platform.entities.Statistics;
import com.novunda.platform.entities.StatisticsTime;
import com.novunda.platform.repositories.StatisticsRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @author Lizy
 */
@Service
@Transactional
public class StatisticsService extends BaseService {
    /**
     * 设备区间最小值
     */
    private static final int MIN_LIVELY_TIME = 24;
    /**
     * 设备区间最大值
     */
    private static final int MAX_LIVELY_TIME = 720;
    /**
     * 审计提示最小值
     */
    private static final int MIN_RECORD_TIME = 1;
    /**
     * 审计提示最大值
     */
    private static final int MAX_RECORD_TIME = 1000000;
    /**
     * 一小时秒数
     */
    private static final int HOUR_SECOND = 3600;
    /**
     * 一分钟秒数
     */
    private static final int MINUTE_SECOND = 60;
    /**
     * 最大时间段个数
     */
    private static final int MAX_STATIC_TIME_SIZE = 5;
    /**
     * 时间格式表达式
     */
    private static final String TIME_FORMAT = "^([0-1][0-9]|[2][0-4]):([0-5][0-9])$";
    /**
     * 活跃区间
     */
    private static final String ERROR_LIVELY_TIME = "活跃区间值必须在不小于24不大于720!";
    /**
     * 审计最大值错误
     */
    private static final String ERROR_RECORD_NUMBER = "审计最大值必须在不小于0不大于1000000之间!";
    /**
     * 审计告警值错误
     */
    private static final String ERROR_RECORD_ALARM = "审计告警值必须在不小于0不大于审计最大值之间!";
    /**
     * 时间格式错误
     */
    private static final String ERROR_TIME_FORMAT = "时间格式填写错误,请按照00:00格式填写!";
    /**
     * 名称不能有重复
     */
    private static final String ERROR_NAME = "名称不能有重复!";
    /**
     * 时间段配置时间错误
     */
    private static final String ERROR_TIME_SECTION = "后时段需大于前时段!";
    /**
     * 时间值错误
     */
    private static final String ERROR_TIME = "填写时间错误,结束时间大于开始时间!";
    /**
     * 时间段不能为空
     */
    private static final String ERROR_TIME_EMPTY = "时间段不能为空!";
    /**
     * 时间段不能大于5条
     */
    private static final String ERROR_TIME_SIZE = "时间段不能大于5条!";
    /**
     * 时间段不能为空
     */
    private static final String ERROR_TIME_NAME_EMPTY = "时间段名称不能为空!";
    /**
     * 字段名称为空时字符串
     */
    private static final String ERROR_NAME_EMPTY = "请输入时间段名称";

    @Autowired
    private StatisticsRepository statisticsRepository;

    /**
     * 获取默认采集时间配置
     *
     * @return 默认值
     */
    public Statistics getDefaultStatistics() {
        return (Statistics) handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                List<Statistics> statisticses = statisticsRepository.findAll();
                if (statisticses.size() != 1) {
                    statisticsRepository.delete(statisticses.get(statisticses.size() - 1));
                }
                return statisticses.get(0);
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("修改");
                log.setName("修改配置");
                log.setDescription("恢复默认配置");
                return log;
            }

            @Override
            public boolean forceLog() {
                return true;
            }
        });
    }

    /**
     * 获取采集时间配置
     *
     * @return 采集时间配置model
     */
    public Statistics getStatistics() {
        List<Statistics> statisticses = statisticsRepository.findAll();
        return statisticses.get(statisticses.size() - 1);
    }

    /**
     * 保存采集配置
     *
     * @param statistics 采集配置实体
     * @return 保存的实体
     */
    public Statistics saveStatistics(final Statistics statistics) throws StatisticsException {
        return (Statistics) handler.doHandler(new LogCallBack() {
            @Override
            public Object doHandler() {
                // 检查时间段是否正确
                if (statistics.getStatisticsTimes().size() > MAX_STATIC_TIME_SIZE) {
                    throw new StatisticsException(ERROR_TIME_SIZE);
                }
                // 检查活跃时间
                if ((statistics.getLivelyTime() < MIN_LIVELY_TIME || statistics.getLivelyTime() > MAX_LIVELY_TIME)) {
                    throw new StatisticsException(ERROR_LIVELY_TIME);
                }
                // 检查审计最大值是否正确
                if (statistics.getRecordNumber() < MIN_RECORD_TIME || statistics.getRecordNumber() > MAX_RECORD_TIME) {
                    throw new StatisticsException(ERROR_RECORD_NUMBER);
                }
                // 检测审计告警值是否正确
                if (statistics.getRecordAlarm() < MIN_RECORD_TIME || statistics.getRecordAlarm() > statistics.getRecordNumber()) {
                    throw new StatisticsException(ERROR_RECORD_ALARM);
                }
                List<StatisticsTime> statisticsTimes = statistics.getStatisticsTimes();
                // 判断是否有时间段
                if (null != statisticsTimes && statisticsTimes.size() != 0) {
                    // 校验时间段是否正确
                    checkStatisticsTimes(statisticsTimes);
                } else {
                    throw new StatisticsException(ERROR_TIME_EMPTY);
                }
                List<Statistics> statisticses = statisticsRepository.findAll();
                // 保存新的配置
                Statistics statistics1 = getNewStatistics(statistics);
                if (statisticses.size() != 1) {
                    statisticsRepository.delete(statisticses.get(1));
                }
                return statisticsRepository.save(statistics1);
            }

            @Override
            public Log generate() {
                Log log = new Log();
                log.setType("修改");
                log.setName("修改配置");
                log.setDescription("修改配置");
                return log;
            }

            @Override
            public boolean forceLog() {
                return true;
            }
        });
    }


    /**
     * 检查时间段是否正确
     *
     * @param statisticsTimes 时间段集合
     */

    private void checkStatisticsTimes(List<StatisticsTime> statisticsTimes) throws StatisticsException {
        for (int i = 0; i < statisticsTimes.size(); i++) {
            // 判断时间段名称是否为空
            if (ERROR_NAME_EMPTY.equals(statisticsTimes.get(i).getName())) {
                throw new StatisticsException(ERROR_TIME_NAME_EMPTY);
            }
            // 检查名称是否重复
            if (checkName(statisticsTimes, statisticsTimes.get(i).getName(), i)) {
                throw new StatisticsException(ERROR_NAME);
            }
            StatisticsTime statisticsTime = statisticsTimes.get(i);
            String beginTimeStr = statisticsTime.getBeginTime();
            String endTimeStr = statisticsTime.getEndTime();
            // 判断开始时间和结束时间都为null则为删除字段
            if (null == beginTimeStr && null == endTimeStr) {
                statisticsTimes.remove(i);
                i--;
                continue;
            }
            // 检查时间格式
            if (!checkDateFormat(endTimeStr) || !checkDateFormat(beginTimeStr)) {
                throw new StatisticsException(ERROR_TIME_FORMAT);
            }
            int endTime = getTime2Int(endTimeStr);
            int beginTime = getTime2Int(beginTimeStr);
            // 判断后一个时间比前一个时间的大小
            if (endTime <= beginTime) {
                throw new StatisticsException(ERROR_TIME);
            }
            // 检查当前时间结束值和下个时间段开始值
            checkNextStatisticsTime(statisticsTimes, i, endTime);
        }
    }

    /**
     * 检查时间段名称是否重复
     *
     * @param statisticsTimes 时间段列表
     * @param name            名称
     * @param index           当前名称位置
     * @return true：有重复；false：没重复
     */
    private boolean checkName(List<StatisticsTime> statisticsTimes, String name, int index) {
        // 若名称为空则返回
        if (null == name) {
            return false;
        }
        for (int i = 0; i < statisticsTimes.size(); i++) {
            // 判断当前名称是否与传入名称相同并且位置不同
            if (name.equals(statisticsTimes.get(i).getName()) && i != index) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检查当前时间与下个时间段是否重叠
     *
     * @param statisticsTimes 时间段列表
     * @param i               当前时间段位置
     * @param endTime         结束时间
     */

    private void checkNextStatisticsTime(List<StatisticsTime> statisticsTimes,
                                         int i, int endTime) throws StatisticsException {
        // 判断集合元素不为1且不是最后一个值
        if (statisticsTimes.size() > 1 && i < statisticsTimes.size() - 1) {
            int nextIndex = i + 1;
            StatisticsTime modelNext = statisticsTimes.get(nextIndex);
            // 判断开始时间和结束时间都为null则为删除字段
            // 检查时间格式
            if (!checkDateFormat(modelNext.getBeginTime())) {
                throw new StatisticsException(ERROR_TIME_FORMAT);
            }
            int nextBeginTime = getTime2Int(modelNext.getBeginTime());
            // 判断下个时间段时间是否大于当前结束值
            if (nextBeginTime < endTime) {
                throw new StatisticsException(ERROR_TIME_SECTION);
            }
        }
    }

    /**
     * 判断格式是否正确
     *
     * @param time 时间
     * @return 时间格式正确或者错误
     */
    private static boolean checkDateFormat(final String time) {
        return Pattern.compile(TIME_FORMAT).matcher(time).matches();
    }

    /**
     * 把字符串时间转换为int
     *
     * @param time 字符串时间
     * @return int 时间毫秒数
     */
    private static int getTime2Int(final String time) {
        String[] myOneTime = time.split(":");
        int hour = Integer.parseInt(myOneTime[0]);
        int min = Integer.parseInt(myOneTime[1]);
        return hour * HOUR_SECOND + min
                * MINUTE_SECOND;
    }

    /**
     * 创建新的采集配置信息数据
     *
     * @param statistics 采集配置信息
     * @return 新的采集配置对象
     */
    private Statistics getNewStatistics(Statistics statistics) {
        Statistics statistics1;
        List<StatisticsTime> statisticsTimeList = statistics.getStatisticsTimes();
        List<StatisticsTime> newList = new ArrayList<>();
        statistics1 = new Statistics();
        statistics1.setLivelyTime(statistics.getLivelyTime());
        statistics1.setRecordStatus(statistics.getRecordStatus());
        statistics1.setRecordNumber(statistics.getRecordNumber());
        statistics1.setRecordAlarm(statistics.getRecordAlarm());
        statistics1.setStatisticsTimes(newList);
        for (StatisticsTime aStatisticsTimeList : statisticsTimeList) {
            StatisticsTime statisticsTime = new StatisticsTime();
            statisticsTime.setName(aStatisticsTimeList.getName());
            statisticsTime.setBeginTime(aStatisticsTimeList.getBeginTime());
            statisticsTime.setEndTime(aStatisticsTimeList.getEndTime());
            statisticsTime.setStatistics(statistics1);
            newList.add(statisticsTime);
        }
        return statistics1;
    }
}
